import { format, getUtcTimestampFromLocalDatetime } from '@/utils/date'
import { ConsoleLogger, LOGLEVEL } from '@/utils/debug-log'
import dayjs from 'dayjs'
import {
  CollPromiseObj,
  createPromiseObj,
  ERecorderBaseStatus,
  ERecorderStatus,
  RecordTime,
} from './index'
import { CallBackHandel, RecorderTime } from './recorderTime'

const console = new ConsoleLogger('视频录制管理')

export interface IRecordManage {
  status: ERecorderStatus
  /**流 */
  audioStream: MediaStream | null
  videoStream: MediaStream | null
  /**录制类 */
  audioRecorder: MediaRecorder | null
  videoRecorder: MediaRecorder | null
  /**初始化 */
  initAudio: () => void | never
  initVideo: (canvas: HTMLCanvasElement) => void
  /** 开始录制 */
  start: () => void
  /** 停止录制*/
  stop: () => void
  /** 暂停录制 */
  pause: () => void
  /**重新开始录制 */
  restart: () => void
  /**上传 */
  getUploadParam: (name: string) => void
  /**恢复状态 */
  flyback: () => void
}
/**音频mime值 */
export const AudioMimeType = MediaRecorder.isTypeSupported('audio/webm;codecs=opus')
  ? 'audio/webm;codecs=opus'
  : 'audio/webm'

/**视频mime值 */
export const VideoMimeType = MediaRecorder.isTypeSupported('video/webm;codecs=h264,opus')
  ? 'video/webm;codecs=h264,opus'
  : 'video/webm;'

/**
 * 错误值枚举
 */
export enum EErrorDic {
  DefalutError = '抱歉出错了',
  AudioAuthError = '录像音频权限失败',
  VideoError = '录像视频生成失败',
  CreateAudioError = '录像音频生成失败',
  CreateVideoError = '录像音视频上传失败',
  PauseError = '暂停录制失败',
  StopError = '停止录像录制失败',
  StartError = '停止录像录制失败',
  Restart = '重新开始录制错误',
  RecordError = '录像异常需重新录制',
  InitRecordTimeError = '未初始化时间计时器',
  UpRecordMissFileError = '录像停止后长时间未获取到音视频文件流',
}

/**
 * EErrorDic的key
 */
export enum EErrorDicKey {
  DefalutError = 'DefalutError',
  AudioAuthError = 'AudioAuthError',
  VideoError = 'VideoError',
  CreateAudioError = 'CreateAudioError',
  CreateVideoError = 'CreateVideoError',
  PauseError = 'PauseError',
  StopError = 'StopError',
  StartError = 'StartError',
  RecordError = 'RecordError',
  Restart = 'Restart',
  InitRecordTimeError = 'InitRecordTimeError',
  /** 录像停止后长时间未获取到音视频文件流*/
  UpRecordMissFileError = 'UpRecordMissFileError',
}
/**
 * RecordError
 * 异常处理分类
 */
export class RecordError extends Error {
  constructor(public code: keyof typeof EErrorDic, message = '') {
    super(message)
    this.name = this.constructor.name
    this.code = code
  }
  static createRecordError(statusCode: keyof typeof EErrorDic) {
    const message = EErrorDic[statusCode]
    console.log(message, LOGLEVEL.error)
    if (message) {
      return new RecordError(statusCode, message)
    } else {
      return new RecordError(statusCode, '抱歉出错了')
    }
  }
}
/**
 * RecordManage 画板录制
 */
export class RecordManage implements IRecordManage {
  status: ERecorderStatus = ERecorderStatus.none
  audioStream: MediaStream | null = null
  videoStream: MediaStream | null = null
  videoRecorder: MediaRecorder | null = null
  audioRecorder: MediaRecorder | null = null
  private audioChunk: Array<Blob> = []
  private videoChunk: Array<Blob> = []
  /**上传等待 */
  private uploadPromiseAudioObj: CollPromiseObj<Blob | boolean> | null = null
  private uploadPromiseVideoObj: CollPromiseObj<Blob | boolean> | null = null
  /**时间记录 */
  re: RecorderTime<RecordManage> | null = null
  /**
   *
   * @param canvas 录像canvas
   * @param callBackHandel  回调用CallBackHandel
   *
   */
  constructor(canvas: HTMLCanvasElement) {
    this.initVideo(canvas)
  }
  /**初始化录制计时器，必须在开始录制之前初始化完成 */
  initRecordTime = (callBackHandel: CallBackHandel<RecordManage>) => {
    //默认6分钟
    this.re = new RecorderTime<RecordManage>(6, {
      content: this,
      fn: {
        start: callBackHandel.start,
        every1s: callBackHandel.every1s,
        finish: callBackHandel.finish,
      },
    })
  }

  initAudio = async () => {
    try {
      console.log('开始初始化音频对象')
      this.audioStream = await navigator.mediaDevices.getUserMedia({
        audio: true,
      })
      this.audioRecorder = new MediaRecorder(this.audioStream!, {
        mimeType: AudioMimeType,
      })
      this.audioRecorder!.ondataavailable = (e: BlobEvent) => {
        this.audioChunk.push(e.data)
      }
      this.audioRecorder!.onstop = () => {
        if (this.uploadPromiseAudioObj) {
          const blob = new Blob(this.audioChunk, {
            type: this.audioChunk[0].type,
          })
          this.uploadPromiseAudioObj.resolve(blob)
        }
      }
      console.log('完成初始化音频对象')
    } catch (e) {
      throw RecordError.createRecordError(EErrorDicKey.AudioAuthError)
    }
  }
  initVideo = (canvas: HTMLCanvasElement) => {
    try {
      console.log('开始初始化视频')
      this.videoStream = canvas.captureStream(25)
      this.videoRecorder = new MediaRecorder(this.videoStream!, {
        mimeType: VideoMimeType,
      })
      this.videoRecorder!.ondataavailable = (e: BlobEvent) => {
        this.videoChunk.push(e.data)
      }

      this.videoRecorder!.onstop = () => {
        if (this.uploadPromiseVideoObj) {
          const blob = new Blob(this.videoChunk, {
            type: this.videoChunk[0].type,
          })
          this.uploadPromiseVideoObj.resolve(blob)
        }
      }
      console.log('完成视频初始化')
    } catch (e) {
      throw RecordError.createRecordError(EErrorDicKey.VideoError)
    }
  }
  /**初始化 */
  flyback = () => {
    this.uploadPromiseAudioObj = null
    this.uploadPromiseVideoObj = null
    if (this.audioRecorder?.state !== ERecorderBaseStatus.inactive) {
      this.audioRecorder?.stop()
    }
    if (this.videoRecorder?.state !== ERecorderBaseStatus.inactive) {
      this.videoRecorder?.stop()
    }
    // this.audioRecorder?.stop();
    // this.videoRecorder?.stop();
    this.audioChunk = []
    this.videoChunk = []
    this.status = ERecorderStatus.none
  }

  checkAndIniAudio = async () => {
    if (!this.re) {
      throw RecordError.createRecordError(EErrorDicKey.InitRecordTimeError)
    }
    try {
      //音视频
      await this.initAudio()
    } catch (e) {
      throw RecordError.createRecordError(EErrorDicKey.AudioAuthError)
    }
  }
  start = async () => {
    try {
      if (!this.re) {
        throw RecordError.createRecordError(EErrorDicKey.InitRecordTimeError)
      }
      console.log('开始录制')
      this.status = ERecorderStatus.start
      this.re.startTime() //开始计时
      console.log('开始计时总录制时间', {
        recordMaxTimeSpan: this.re.recordMaxTimeSpan,
        RecordStartTime: format(new Date(this.re.getRecordStartTime), 'yyyy-MM-dd hh:mm:ss'),
      })
      this.audioRecorder?.start(this.re.getRecordStartTime)
      this.videoRecorder?.start(this.re.getRecordStartTime)
    } catch (e) {
      if ((e as RecordError).code == EErrorDicKey.InitRecordTimeError) {
        throw RecordError.createRecordError(EErrorDicKey.InitRecordTimeError)
      }
      if ((e as RecordError).code == EErrorDicKey.AudioAuthError) {
        throw RecordError.createRecordError(EErrorDicKey.AudioAuthError)
      }
      throw RecordError.createRecordError(EErrorDicKey.StartError)
    }
  }
  stop = () => {
    try {
      console.log('开始结束录制')
      if (!this.re) {
        throw RecordError.createRecordError(EErrorDicKey.InitRecordTimeError)
      }
      this.re.clearInterval()
      this.audioRecorder?.stop()
      this.videoRecorder?.stop()
      this.re.reset()
      this.audioStream!.getTracks().forEach(function (track) {
        track.stop()
      })
      this.status = ERecorderStatus.none
      console.log('开始计时总录制时间', {
        recordMaxTimeSpan: this.re.recordMaxTimeSpan,
        RecordStartTime: format(new Date(this.re.getRecordStartTime), 'yyyy-MM-dd hh:mm:ss'),
      })
    } catch (e) {
      throw RecordError.createRecordError(EErrorDicKey.StopError)
    }
  }
  pause = async () => {
    console.log('暂停录制')
    try {
      if (!this.re) {
        throw RecordError.createRecordError(EErrorDicKey.InitRecordTimeError)
      }
      this.status = ERecorderStatus.pause
      await this.audioRecorder?.pause()
      await this.videoRecorder?.pause()
      this.re!.clearInterval()
    } catch (e) {
      throw RecordError.createRecordError(EErrorDicKey.PauseError)
    }
  }
  restart = async () => {
    console.log('继续录制')
    try {
      if (!this.re) {
        throw RecordError.createRecordError(EErrorDicKey.InitRecordTimeError)
      }
      this.status = ERecorderStatus.restart
      /**重启刷新 */
      this.re!.reflashInterval()
      await this.audioRecorder?.resume()
      await this.videoRecorder?.resume()
    } catch (e) {
      throw RecordError.createRecordError(EErrorDicKey.Restart)
    }
  }

  getUploadParam = async (name: string) => {
    try {
      console.log('开始上传录制音视频', {
        videoCount: this.videoChunk.length,
        audioCount: this.audioChunk.length,
      })
      this.uploadPromiseAudioObj = createPromiseObj<Blob | boolean>()
      this.uploadPromiseVideoObj = createPromiseObj<Blob | boolean>()

      setTimeout(() => {
        if (this.videoChunk.length === 0) {
          console.log('生成文件超时，超时时间暂时定为10s')
          this.uploadPromiseAudioObj!.resolve(false)
          this.uploadPromiseVideoObj!.resolve(false)
        }
      }, 10000)
      /**触发停止动作 */
      this.stop()
      /**等待获取音视频 */
      const audioBlob = await this.uploadPromiseAudioObj.promiseObj
      const videoBlob = await this.uploadPromiseVideoObj.promiseObj
      if (!audioBlob && !videoBlob) {
        throw RecordError.createRecordError(EErrorDicKey.UpRecordMissFileError)
      } else {
        const formData = new FormData()

        formData.append('title', name)
        formData.append('ctimeFormat', dayjs(Date.now()).format('YYYY-MM-DD HH:mm:ss'))
        formData.append('audioData', audioBlob as Blob)
        formData.append('videoData', videoBlob as Blob)

        // const url = URL.createObjectURL(videoBlob);
        // // 视频下载
        // const a = document.createElement('a');
        // a.href = url;
        // a.download = 'video.webm';
        // a.click();

        return formData
        // axios({url:'', method: 'post',data:formData,headers:{},onUploadProgress:progress})
        //to do somthing
      }
    } catch (e) {
      console.log('上传录制错误', {
        videoCount: this.videoChunk.length,
        audioCount: this.audioChunk.length,
      })
    }
  }
}
